/*!
\copyright  Copyright (c) 2023 Qualcomm Technologies International, Ltd.
            All Rights Reserved.
            Qualcomm Technologies International, Ltd. Confidential and Proprietary.
\file
\brief      List debug helpers
*/

#include "list.h"
#include "list_debug.h"

#ifndef DISABLE_LIST_TRACE

/** The number of nodes that can be tracked by the LIST_TRACE_DEBUG functionality */
#ifndef LIST_TRACE_OWNERS_MAX
#define LIST_TRACE_OWNERS_MAX       (256)
#endif

typedef struct list_owner_t {
    const void* node;
    list_t list;
    const char* owner;
} list_owner_t;

static list_owner_t list_trace_owners[LIST_TRACE_OWNERS_MAX];

static list_node_trace_stats_t list_node_trace_stats;

static void checkAndUpdateOwnerEntry(const list_t new_list, const list_t old_list, list_owner_t *owner_entry)
{
    if ((old_list != new_list) && (owner_entry->list == old_list))
    {
        owner_entry->list = new_list;
    }
}

static bool ownerEntryIsFree(list_owner_t *owner_entry)
{
    return !owner_entry->node && !owner_entry->list && !owner_entry->owner;
}

static bool ownerEntryExists(list_owner_t *owner_entry, const list_t list, const void *node, const char *owner)
{
    return (owner_entry->node == node) && (owner_entry->list == list) && (owner_entry->owner == owner);
}

#endif /* DISABLE_LIST_TRACE */

void ListDebugAddNodeTrace(const list_t new_list, const list_t old_list, const void *new_node, const char *owner)
{
#ifndef DISABLE_LIST_TRACE
    for (size_t i = 0; i < LIST_TRACE_OWNERS_MAX; i++)
    {
        list_owner_t *owner_entry = list_trace_owners + i;
        checkAndUpdateOwnerEntry(new_list, old_list, owner_entry);
        
        if(ownerEntryExists(owner_entry, new_list, new_node, owner))
        {
            return;
        }

        if (ownerEntryIsFree(owner_entry))
        {
            owner_entry->node = new_node;
            owner_entry->list = new_list;
            owner_entry->owner = owner;

            list_node_trace_stats.current_count++;
            if (list_node_trace_stats.current_count > list_node_trace_stats.peak_count)
            {
                list_node_trace_stats.peak_count = list_node_trace_stats.current_count;
            }
            DEBUG_LOG_V_VERBOSE("Adding node node 0x%p. Stats: current_count=%u, peak_count=%u", owner_entry->node, list_node_trace_stats.current_count, list_node_trace_stats.peak_count);
            return;
        }
    }
    DEBUG_LOG_WARN("Unable to find a free slot to trace node. list=0x%p, node=0x%p, owner=%s", new_list, new_node, owner);
#else
    UNUSED(new_list);
    UNUSED(old_list);
    UNUSED(new_node);
    UNUSED(owner);
#endif /* DISABLE_LIST_TRACE */
}

/* This needs to be changed to cope with list array implementations
    Either track the element index into the list instead or search by the list instead
    Or maybe if remove_node is null don't do anything and that would possibly work...
*/
void ListDebugRemoveNodeTrace(const list_t new_list, const list_t old_list, const void* remove_node)
{
#ifndef DISABLE_LIST_TRACE
    for (size_t i = 0; i < LIST_TRACE_OWNERS_MAX; i++)
    {
        list_owner_t *owner_entry = list_trace_owners + i;
        checkAndUpdateOwnerEntry(new_list, old_list, owner_entry);

        if (remove_node && (owner_entry->node == remove_node))
        {
            memset(&list_trace_owners[i], 0, sizeof(list_trace_owners[i]));
            list_node_trace_stats.current_count--;
            DEBUG_LOG_V_VERBOSE("Deleting node 0x%p. Stats: current_count=%u, peak_count=%u", owner_entry->node, list_node_trace_stats.current_count, list_node_trace_stats.peak_count);
        }
    }
#else
    UNUSED(remove_node);
#endif /* DISABLE_LIST_TRACE */
}

void ListDebugDestroyTrace(const list_t list)
{
#ifndef DISABLE_LIST_TRACE
    for (size_t i = 0; i < LIST_TRACE_OWNERS_MAX; i++)
    {
        list_owner_t* trace_owner = list_trace_owners + i;
        if (trace_owner->list == list)
        {
            memset(trace_owner, 0, sizeof(*trace_owner));
            list_node_trace_stats.current_count--;
        }
    }
#else
    UNUSED(list);
#endif /* DISABLE_LIST_TRACE */
}

const list_node_trace_stats_t* ListDebugGetNodeTraceStats(void)
{
    return &list_node_trace_stats;
}

#ifdef SIMULATOR

void ListDebugResetNodeTraceStats(void)
{
    memset(&list_node_trace_stats, 0, sizeof(list_node_trace_stats));
}

#endif